package insert

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

type User struct {
	gorm.Model
	Name     string
	Age      int
	Birthday time.Time
}

type Person struct {
	gorm.Model
	Name    string
	Age     int
	Address Address
}
type Address struct {
	gorm.Model
	Post string
}

type Product struct {
	gorm.Model
}

type Order struct {
	gorm.Model
}

func ConnectMysql() (*gorm.DB, error) {

	// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
	//todo   username:password@protocol(address)/dbname?param=value
	dsn := "root:2868463718@tcp(127.0.0.1:3306)/go-gorm-learn?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		//Logger:logger.Default.LogMode(logger.Info),   //todo 打印所有sql
	})
	if err != nil {
		panic(err)
	}
	return db, nil
}

func Create(db *gorm.DB) {

	user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}

	/**
	todo     SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'go-gorm-learn%' ORDER BY SCHEMA_NAME='go-gorm-learn' DESC limit 1
	todo 1.  SELECT count(*) FROM information_schema.tables WHERE table_schema = 'go-learn' AND table_name = 'users' AND table_type ='BASE TABLE'
	todo 2. CREATE TABLE `users` (`id` bigint unsigned AUTO_INCREMENT,`created_at` datetime(3) NULL,`updated_at` datetime(3) NULL,`deleted_at` datetime(3) NULL,`name` longtext,`age` bigint,`birthday` datetime(3) NULL,PRIMARY KEY (`id`),INDEX idx_users_deleted_at (`deleted_at`))
	*/
	// 自动迁移，刷新表结构  ，如果数据库没有创建表，就使用结构体创建表结构，如果存在表，就刷新表结构
	db.Debug().AutoMigrate(&user)

	/**
	INSERT INTO `users` (`created_at`,`update
	d_at`,`deleted_at`,`name`,`age`,`birthday`) VALUES ('2021-08-20 16:08:57.212','2
	021-08-20 16:08:57.212',NULL,'Jinzhu',18,'2021-08-20 16:08:57.202')
	*/
	result := db.Debug().Create(&user)

	fmt.Println(result.Error)
	fmt.Println(result.RowsAffected)
	fmt.Println(result.Row()) // SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL AND `users`.`id` = 1
	fmt.Println()
}

func AutoMigrate(db *gorm.DB) {
	// GORM 本身提供了 AutoMigrate 功能以及 Migrator 提供的 todo DDL 接口，但 GORM 更加专注于 ORM 层面，
	//所以在 ORM Schema Version Control（数据库版本控制）有所欠缺。而 gormigrate 就是一个轻量化的 Schema Migration Helper（迁移助手），
	//基于 GORM AutoMigrate 和 Migrator 进行封装，用于弥补这一块的缺失。

	// GORM 的 AutoMigrate() 方法用于自动迁移 ORM 的 Schemas。所谓 “迁移” 就是刷新数据库中的表格定义，使其保持最新（只增不减）。

	// AutoMigrate 会创建（新的）表、缺少的外键、约束、列和索引，并且会更改现有列的类型（如果其大小、精度、是否为空可更改的话）。但不会删除未使用的列，以保护现存的数据。

	// GORM支持Migration特性，支持根据Go Struct结构自动生成对应的表结构。
	// 注意：GORM 的AutoMigrate函数，仅支持建表，不支持修改字段和删除字段，避免意外导致丢失数据。

	// 1.自动建表
	// 通过AutoMigrate函数可以快速建表，如果表已经存在不会重复创建。
	// 根据User结构体，自动创建表结构.
	db.AutoMigrate(&User{})

	// 一次创建User、Product、Order三个结构体对应的表结构
	db.AutoMigrate(&User{}, &Product{}, &Order{})

	// 可以通过Set设置附加参数，下面设置表的存储引擎为InnoDB
	db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})

	// 2.Schema方法

	// 2.1 检测表是否存在
	// 检测User结构体对应的表是否存在
	db.Migrator().HasTable(&User{})

	// 检测表名users是否存在
	db.Migrator().HasTable("users")

	// 2.2 建表
	// 根据User结构体建表
	db.Migrator().CreateTable(&User{})

	// 2.3 删除表
	// 删除User结构体对应的表
	db.Migrator().DropTable(&User{})

	// 删除表名为users的表
	db.Migrator().DropTable("users")

	// 2。4 删除字段
	// 删除User结构体对应表中的description字段
	db.Migrator().DropColumn(&User{}, "Name")
	db.Migrator().AddColumn(&User{}, "Name")
	db.Migrator().HasColumn(&User{}, "Name")
	db.Migrator().RenameColumn(&User{}, "Name", "Name1")
	db.Migrator().AlterColumn(&User{}, "Name") // todo 修改name字段，实际上就是修改name字段类型等信息
	db.Migrator().ColumnTypes(&User{})

	/**

	  type ColumnType interface {
	  	Name() string
	  	DatabaseTypeName() string
	  	Length() (length int64, ok bool)
	  	DecimalSize() (precision int64, scale int64, ok bool)
	  	Nullable() (nullable bool, ok bool)
	  }

	  type Migrator interface {
	  	// AutoMigrate
	  	AutoMigrate(dst ...interface{}) error

	  	// Database
	  	CurrentDatabase() string
	  	FullDataTypeOf(*schema.Field) clause.Expr

	  	// Tables
	  	CreateTable(dst ...interface{}) error
	  	DropTable(dst ...interface{}) error
	  	HasTable(dst interface{}) bool
	  	RenameTable(oldName, newName interface{}) error
	  	GetTables() (tableList []string, err error)

	  	// Columns
	  	AddColumn(dst interface{}, field string) error
	  	DropColumn(dst interface{}, field string) error
	  	AlterColumn(dst interface{}, field string) error
	  	MigrateColumn(dst interface{}, field *schema.Field, columnType ColumnType) error
	  	HasColumn(dst interface{}, field string) bool
	  	RenameColumn(dst interface{}, oldName, field string) error
	  	ColumnTypes(dst interface{}) ([]ColumnType, error)

	  	// Views
	  	CreateView(name string, option ViewOption) error
	  	DropView(name string) error

	  	// Constraints
	  	CreateConstraint(dst interface{}, name string) error
	  	DropConstraint(dst interface{}, name string) error
	  	HasConstraint(dst interface{}, name string) bool

	  	// Indexes
	  	CreateIndex(dst interface{}, name string) error
	  	DropIndex(dst interface{}, name string) error
	  	HasIndex(dst interface{}, name string) bool
	  	RenameIndex(dst interface{}, oldName, newName string) error
	  }

	*/

}

func Insert(db *gorm.DB) {
	user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}

	// insert all
	// 为什么要传递指针呢？主要是gorm 里面需要反射
	db.Create(&user)

	// insert some fields
	// 插入部分字段
	// Select specify fields that you want when querying, creating, updating
	//func (db *DB) Select(query interface{}, args ...interface{}) (tx *DB)
	//todo 只插入 选择的字段进行插入， 但是会默认把更新字段和 创建时间字段也插入
	db.Debug().Select("Name", "Age", "CreatedAt").Create(&user) // INSERT INTO `users` (`created_at`,`updated_at`,`name`,`age`) VALUES ('2022-09-20 13:08:51.481','2022-09-20 13:08:51.481','Jinzhu',18)

	// insert ignore some fields
	// 插入时忽略部分字段
	db.Debug().Omit("Name", "CreatedAt").Create(&user) // INSERT INTO `users` (`updated_at`,`deleted_at`,`age`,`birthday`) VALUES ('2022-09-20 13:14:53.524',NULL,18,'2022-09-20 13:14:53.521')

}

// BatchInsert 要有效地插入大量记录，请将一个 slice 传递给 Create 方法。 GORM 将生成单独一条SQL语句来插入所有数据，并回填主键的值，钩子方法也会被调用。
// todo 注意该方法会把切片中的所有的记录全都 一次性插入， 可以使用CreateInBatches 分批创建， 可以指定每次一次性插入的数量
func BatchInsert(db *gorm.DB) {
	var users = []User{{Name: "jinzhu1", Birthday: time.Now()}, {Name: "jinzhu2", Birthday: time.Now()}, {Name: "jinzhu3", Birthday: time.Now()}}

	/**
	INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`)
		VALUES
			('2022-09-20 14:03:52.766','2022-09-20 14:03:52.766',NULL,'jinzhu1',0,'2022-09-20 14:03:52.763'),
			('2022-09-20 14:03:52.766','2022-09-20 14:03:52.766',NULL,'jinzhu2',0,'2022-09-20 14:03:52.763'),
			('2022-09-20 14:03:52.766','2022-09-20 14:03:52.766',NULL,'jinzhu3',0,'2022-09-20 14:03:52.763')
	*/
	//db.Debug().Create(&users)
	//
	//for _, user := range users {
	//	println(user.ID) // 1,2,3
	//}

	/**

	todo CreateInBatches 批量插入，如果数组10个元素， 每次批量插入2个记录

	INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`)
		VALUES
			('2022-09-20 14:06:29.195','2022-09-20 14:06:29.195',NULL,'jinzhu1',0,'2022-09-20 14:06:29.191'),
			('2022-09-20 14:06:29.195','2022-09-20 14:06:29.195',NULL,'jinzhu2',0,'2022-09-20 14:06:29.191')


	INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`)
		VALUES
		('2022-09-20 14:06:29.201','2022-09-20 14:06:29.201',NULL,'jinzhu3',0,'2022-09-20 14:06:29.191')

	*/
	db.Debug().CreateInBatches(&users, 2)
}

// MapInsert 根据map 创建对象
//  根据 map 创建记录时，association 不会被调用，且主键也不会自动填充
func MapInsert(db *gorm.DB) {

	// INSERT INTO `users` (`age`,`birthday`,`name`) VALUES (18,'2022-09-20 14:22:48.466','jinzhu')
	// todo createtime  updatetime  都不会被填充
	db.Debug().Model(&User{}).Create(map[string]interface{}{
		"Name": "jinzhu", "Age": 18, "Birthday": time.Now(),
	})

	// batch insert from `[]map[string]interface{}{}`
	// INSERT INTO `users` (`age`,`birthday`,`name`) VALUES (18,'2022-09-20 14:25:53.922','jinzhu_1'),(20,'2022-09-20 14:25:53.922','jinzhu_2'),(20,'2022-09-20 14:25:53.922','jinzhu_2')
	db.Debug().Model(&User{}).Create([]map[string]interface{}{
		{"Name": "jinzhu_1", "Age": 18, "Birthday": time.Now()},
		{"Name": "jinzhu_2", "Age": 20, "Birthday": time.Now()},
		{"Name": "jinzhu_2", "Age": 20, "Birthday": time.Now()},
	})

	// INSERT INTO `users` (`age`,`birthday`,`name`) VALUES (18,'2022-09-20 14:25:53.927','jinzhu_1'),(20,'2022-09-20 14:25:53.927','jinzhu_2')
	// INSERT INTO `users` (`age`,`birthday`,`name`) VALUES (20,'2022-09-20 14:25:53.927','jinzhu_2')
	db.Debug().Model(&User{}).CreateInBatches([]map[string]interface{}{
		{"Name": "jinzhu_1", "Age": 18, "Birthday": time.Now()},
		{"Name": "jinzhu_2", "Age": 20, "Birthday": time.Now()},
		{"Name": "jinzhu_2", "Age": 20, "Birthday": time.Now()},
	}, 2)
}

// AssociationsInsert 级联创建
func AssociationsInsert(db *gorm.DB) {

	db.Debug().AutoMigrate(&Person{}, &Address{})
	// 创建关联数据时，如果关联值是非零值，这些关联会被 upsert，且它们的 Hook 方法也会被调用
	db.Debug().Create(&Person{
		Name: "zhangsan",
		Age:  14,
		Address: Address{
			Post: "441206",
		},
	})
}
